using System;
using System.Collections.Generic;
using System.Text;
using GraphConstructor;
//using GraphConstructor.Controls;

namespace PhoneNetwork
{
    public class PhoneNetwork : IGraph
    {
        private bool _isWeighted = true;
        private bool _isDirected = true;
        private List<Edge> _edges = new List<Edge>();
        private List<Node> _nodes = new List<Node>();
        private List<Node> _startNodes = new List<Node>();
        internal static frmStatistics _frmStatistics;

        public string Name
        { get { return "Phone Network"; } }
        public string Description
        { get { return "Phone Network"; } }
        public string Complexty
        { get { return "O ( )"; } }
        public bool IsWeighted
        {
            get { return _isWeighted; }
            set { _isWeighted = value; }
        }
        public bool IsDirected
        {
            get { return _isDirected; }
            set { _isDirected = value; }
        }
        public List<Edge> Edges
        {
            get { return _edges; }
            set { _edges = value; }
        }
        public List<Node> Nodes
        {
            get { return _nodes; }
            set { _nodes = value; }
        }
        public List<Node> StartNodes
        {
            get { return _startNodes; }
            set { _startNodes = value; }
        }

        public List<Edge> MakePhoneCall(Node From, Node To, bool Starter)
        {
            //if (!Nodes.Contains(From) || !Nodes.Contains(To))
            //    return null;
            if ((From.Data as Phone).Visited)
                return null;
            if (From == To)
            {
                (To.Data as Phone).IsBusy = true;
                return new List<Edge>();
            }
            (From.Data as Phone).Visited = true;
            foreach (Edge e in From.Adj)
            {
                Wire wire = e.Data as Wire;
                if (wire.Capacity - wire.InUse <= 0)
                    continue;
                List<Edge> phoneCall = MakePhoneCall(e.NodeTo, To, false);
                if (phoneCall != null)
                {
                    phoneCall.Insert(0, e);
                    wire.InUse++;
                    (From.Data as Phone).Visited = false;
                    (From.Data as Phone).IsBusy = Starter;
                    return phoneCall;
                }
            }
            (From.Data as Phone).Visited = false;
            return null;
        }
        public bool TryMakePhoneCall(Node From, Node To)
        {
            if (!Nodes.Contains(From) || !Nodes.Contains(To))
                return false;
            if ((From.Data as Phone).Visited)
                return false;
            if (From == To)
            {
                //(To.Data as Phone).IsBusy = true;
                return true;// new List<Edge>();
            }
            (From.Data as Phone).Visited = true;
            foreach (Edge e in From.Adj)
            {
                Wire wire = e.Data as Wire;
                if (wire.Capacity - wire.InUse <= 0)
                    continue;
                bool phoneCall = TryMakePhoneCall(e.NodeTo, To);
                if (phoneCall)
                {
                    //phoneCall.Add(e);
                    wire.InUse++;
                    (From.Data as Phone).Visited = false;
                    //(From.Data as Phone).IsBusy = Starter;
                    return true;// phoneCall;
                }
            }
            (From.Data as Phone).Visited = false;
            return false;
        }
        public void EndPhoneCall(List<Edge> call, Phone Starter, Phone End)
        {
            Starter.IsBusy = false;
            End.IsBusy = false;
            foreach (Edge e in call)
            {
                (e.Data as Wire).InUse--;
                e.Weight = (e.Data as Wire).Capacity - (e.Data as Wire).InUse;

                (e.NodeFrom.Data as Phone).IsBusy = false;
                (e.NodeFrom.Data as Phone).Visited = false;
                (e.NodeTo.Data as Phone).IsBusy = false;
                (e.NodeTo.Data as Phone).Visited = false;
            }
        }

        internal class RandomCall : Random
        {
            List<Node> _phones;
            private Node _start;

            public Node Start
            {
                get { return _start; }
                set { _start = value; }
            }
            private Node _end;

            public Node End
            {
                get { return _end; }
                set { _end = value; }
            }

            public Node getNode(Node start, int len)
            {
                if (start.Adj.Count == 0)
                    if (len == 0)
                    {
                        if ((start.Data as Phone).IsBusy)
                            return null;
                        else
                            return start;
                    }
                    else
                        return null;
                if (len == 0)
                    if ((start.Data as Phone).IsBusy)
                        return null;
                    else
                        return start;
                int rand = base.Next() % start.Adj.Count;
                Node res;
                if (start.Adj[rand].Weight > 0 && (res = getNode(start.Adj[rand].NodeTo, len - 1)) != null)
                    if ((res.Data as Phone).IsBusy)
                        return null;
                    else
                        return res;
                foreach (Edge e in start.Adj)
                    if (e.Weight > 0)
                        if ((res = getNode(e.NodeTo, len - 1)) != null)
                            if ((res.Data as Phone).IsBusy)
                                return null;
                            else
                                return res;
                return null;
            }
            public override int Next()
            {
                int start = base.Next() % _phones.Count;
                int len = base.Next() % _phones.Count;
                Node res = null;
                if (!(_phones[start].Data as Phone).IsBusy)
                    res = getNode(_phones[start], len);
                if (res != null)
                {
                    _start = _phones[start];
                    _end = res;
                    return len;
                }
                else
                {
                    for (int i = len; i > 0; i--)
                        for (int j = 0; j < _phones.Count; j++)
                        {
                            if (!(_phones[j].Data as Phone).IsBusy)
                                if ((res = getNode(_phones[j], i)) != null)
                                {
                                    _start = _phones[j];
                                    _end = res;
                                    return i;
                                }
                        }
                }
                return -1;
            }
            public RandomCall(List<Node> Phones)
                : base(2)
            {
                _phones = Phones;
            }
        }
        internal class PhoneNetworkEnumerated : IEnumerator<IStep>
        {
            List<Edge> _edges = new List<Edge>();
            List<List<Edge>> _paths = new List<List<Edge>>();
            RandomCall _randomCall;
            Random _randomAction;
            PhoneNetwork g;
            Node n;
            IStep step;
            int i;

            public IStep Current
            {
                get { return step; }
            }

            public void Dispose()
            {

            }

            object System.Collections.IEnumerator.Current
            {
                get { return step; }
            }

            public bool MoveNext()
            {
                int action = _randomAction.Next() % 2;
                if (action == 0 || _paths.Count == 0)
                {
                    int nextCall = _randomCall.Next();
                    if (nextCall == -1)
                    {
                        step.IsFinal = true;
                        return false;
                    }
                    List<Edge> path = g.MakePhoneCall(_randomCall.Start, _randomCall.End, true);
                    if (path == null)
                    {
                        step.IsFinal = true;
                        return false;
                    }
                    if (path.Count <= 0)
                        return true;
                    _paths.Add(path);
                    string strPath = path[0].NodeFrom.ToString() + "->";
                    //step.State.Nodes.Clear();
                    step.State.Edges.Clear();
                    step.State.Nodes.Add(path[0].NodeFrom);
                    step.State.Nodes.Add(path[path.Count - 1].NodeTo);
                    foreach (Edge e in path)
                    {
                        //step.State.Nodes.Add(e.NodeFrom);
                        //step.State.Nodes.Add(e.NodeTo);
                        e.Weight = (e.Data as Wire).Capacity - (e.Data as Wire).InUse;
                        step.State.Edges.Add(e);
                        strPath += e.NodeTo.ToString();
                        if (path[path.Count - 1] != e)
                             strPath += "->";
                    }
                    _frmStatistics.Add(EventType.StartCall, (path[0].NodeFrom.Data as Phone).Name, (path[path.Count - 1].NodeTo.Data as Phone).Name, strPath);
                }
                else
                {
                    int indToEnd = _randomAction.Next() % _paths.Count;
                    List<Edge> path = _paths[indToEnd];
                    string strPath = path[0].NodeFrom.ToString() + "->";
                    
                    path.ForEach(delegate(Edge e) 
                    {
                        strPath += e.NodeTo.ToString();
                        if (e != path[path.Count - 1])
                            strPath += "->"; 
                    });


                    g.EndPhoneCall(path, path[0].NodeFrom.Data as Phone, path[path.Count - 1].NodeTo.Data as Phone);
                    step.State.Nodes.Remove(path[0].NodeFrom);
                    step.State.Nodes.Remove(path[0].NodeTo);

                    _frmStatistics.Add(EventType.EndCall, (path[0].NodeFrom.Data as Phone).Name, (path[path.Count - 1].NodeTo.Data as Phone).Name, strPath);

                    _paths.RemoveAt(indToEnd);
                }
                return true;
            }
            public void Reset()
            {
                _edges.Clear();
                step = new PhoneNetworkStep();
                step.State = new PhoneNetwork();
                foreach (Edge e in g.Edges)
                    _edges.Add(e);
                _randomCall = new RandomCall(g.Nodes);
                _randomAction = new Random(Environment.TickCount);
                if (_paths != null)
                    foreach (List<Edge> path in _paths)
                        if (path.Count > 0)
                            g.EndPhoneCall(path, path[0].NodeFrom.Data as Phone, path[path.Count - 1].NodeTo.Data as Phone);
                step.State.Nodes.Clear();
                step.State.Edges.Clear();
            }

            public PhoneNetworkEnumerated(PhoneNetwork d)
            {
                g = d;
            }
        }
        internal class PhoneNetworkStep : IStep
        {
            private bool _isFinal;
            private IGraph _state;

            public IGraph State
            {
                get { return _state; }
                set { _state = value; }
            }
            public bool IsFinal
            {
                get { return _isFinal; }
                set { _isFinal = value; }
            }
        }

        public IEnumerator<IStep> GetEnumerator()
        {
            PhoneNetworkEnumerated phoneNetwork = new PhoneNetworkEnumerated(this);
            foreach (Node n in Nodes)
                n.Data = new Phone(n.ID, n);
            foreach (Edge e in Edges)
                e.Data = new Wire(e.Weight);
            foreach (Node n in Nodes)
                for (int i = 0; i < n.Adj.Count; i++)
                {
                    Phone phone = n.Data as Phone;
                    //phone.Propability[i] = 100 / n.Adj.Count;
                }
            if (_frmStatistics != null)
                _frmStatistics.Dispose();
            _frmStatistics = new frmStatistics();
            _frmStatistics.Clear();
            _frmStatistics.Show();
            _frmStatistics.TopMost = true;
            //_frmStatistics.BringToFront();
            return phoneNetwork;
        }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            throw new Exception("The method or operation is not implemented.");
        }
    }
}
